#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/types.h>

#include "progress_bar.h"
#include "log.h"

static int gain_terminal_width(void);

static struct progress_bar entrance_param_data = {
    .mark = DEFAULT_MARK,
    .length = DEFALUT_LENGTH,
    .speed = DEFALUT_SPEED,
    .color = DEFALUT_COLOR,
};

static struct progress_bar progress_bar_data = {
    .mark = DEFAULT_MARK,
    .length = DEFALUT_LENGTH,
    .speed = DEFALUT_SPEED,
    .color = DEFALUT_COLOR,
};


/*
 * @brief	get and ayalysis the entrance param.
 *          Modifies the structure(entrance_param_data).          
 * @param	param_argc: strings'number
 * @param   param_argv: strings'pointer
 * @return	void
 * */
int get_entrance_param(int param_argc, char *param_argv[])
{
    int opt;
    char *str = "m:l:s:c:";

    while ((opt = getopt(param_argc, param_argv, str)) != -1) {
        switch (opt) {
            case 'm':
                if (strlen(optarg) == 1)
                    entrance_param_data.mark = *optarg;
                else {
                    DEBUG(PROGRESS_RED"ERROR: Input wrong mark!\n"PROGRESS_RESETCLOUR);
                    return FAIL;
                }
                break;

            case 'l':
                if (atoi(optarg) < 0 || atoi(optarg) > 256) {
                    DEBUG(PROGRESS_RED"ERROR: Input wrong length!\n"PROGRESS_RESETCLOUR);
                    return FAIL;
                }
                else
                   entrance_param_data.length = atoi(optarg); 
                break;

            case 's':
                if (atoi(optarg) < 0 || atoi(optarg) > 256) {
                    DEBUG(PROGRESS_RED"ERROR: Input wrong speed!\n"PROGRESS_RESETCLOUR);
                    return FAIL;
                }
                else
                   entrance_param_data.speed = atoi(optarg); 
                break;

            case 'c':
                if (atoi(optarg) < 0 || atoi(optarg) > 256) {
                    DEBUG(PROGRESS_RED"ERROR: Input wrong color!\n"PROGRESS_RESETCLOUR);
                    return FAIL;
                }
                else
                   entrance_param_data.color = atoi(optarg); 
                break;

            default:
                break;
        }
    }

    return SUCCESS;
}

/*
 * @brief	Initializing the parameters of the progress bar 
 *          Modifies the structure(progress_bar_data).         
 * @param	mark: the mark of the progress bar, 0 means default.
 * @param	length: the length of the progress bar, 0 means default. range(10 - 100)
 * @param	speed: the speed of the progress bar,  0 means default.  range(0 - 10)
 * @param	color: the color of the progress bar,  0 means default.  range(0 - 4)
 *                 0 default, 1 red, 2 green, 3 yellow, 4 blue
 * @return	void
 * */
int progress_bar_init(char mark, int length, int speed, int color)
{
    /*Init the mark, 0 means default */
    if (mark == 0)
        progress_bar_data.mark = DEFAULT_MARK;
    else
        progress_bar_data.mark = mark;

    /*Init the length, 0 means default */
    if (length == 0)
        progress_bar_data.length = DEFALUT_LENGTH;
    else if (length > LENGTH_MAX){
        printf(PROGRESS_RED"ERROR: The input length is too long! ");
        printf("The scope should be %d - %d .\n"PROGRESS_RESETCLOUR, LENGTH_MIN, LENGTH_MAX);
        return FAIL;
    }
    else if (length < LENGTH_MIN){
        printf(PROGRESS_RED"ERROR: The input length is too short! ");
        printf("The scope should be %d - %d .\n"PROGRESS_RESETCLOUR, LENGTH_MIN, LENGTH_MAX);
        return FAIL;
    }
    else
       progress_bar_data.length = length;

    if (progress_bar_data.length > gain_terminal_width()-15)
         progress_bar_data.length = gain_terminal_width()-15;

    /*Init the speed, 0 means default */
    if (speed == 0)
        progress_bar_data.speed = DEFALUT_SPEED;
    else if (speed > SPEED_MAX){
        printf(PROGRESS_RED"ERROR: The input speed is too fast! ");
        printf("The scope should be %d - %d .\n"PROGRESS_RESETCLOUR, SPEED_MIN, SPEED_MAX);
        return FAIL;
    }
    else if (speed < SPEED_MIN){
        printf(PROGRESS_RED"ERROR: The input speed is too slow! ");
        printf("The scope should be %d - %d .\n"PROGRESS_RESETCLOUR, SPEED_MIN, SPEED_MAX);
        return FAIL;
    }
    else
       progress_bar_data.speed = speed; 

    /*Init the color, 0 means default */
    if (color == 0)
        progress_bar_data.color = DEFALUT_COLOR;
    else if (color > COLOR_MAX || color < COLOR_MIN) {
        printf(PROGRESS_RED"ERROR: Input wrong color!\n");
        printf("The scope should be %d - %d .\n"PROGRESS_RESETCLOUR, COLOR_MIN, COLOR_MAX);
        return FAIL;
    }
    else
       progress_bar_data.color = color;

    return SUCCESS; 
}

int progress_bar_run(void)
{
    char progress_bar[progress_bar_data.length + 2];
    const char* lable = "-\\|/";
    int rate = 0;
    int bar_count=0;

    memset(progress_bar, ' ', sizeof(progress_bar));

    while (rate <= 100) {

        switch (progress_bar_data.color) {
            case NOMAL:
                printf("[  %s] [%d%%] %c\r", progress_bar, rate, lable[rate%4]);
                break;
            case RED:
                printf(PROGRESS_RED"[  %s] [%d%%] %c\r"PROGRESS_RESETCLOUR,
                        progress_bar, rate, lable[rate%4]);
                break;
            case GREEN:
                printf(PROGRESS_GREEN"[  %s] [%d%%] %c\r"PROGRESS_RESETCLOUR, 
                        progress_bar, rate, lable[rate%4]);
                break;
            case YELLOW:
                printf(PROGRESS_YELLOW"[  %s] [%d%%] %c\r"PROGRESS_RESETCLOUR, 
                        progress_bar, rate, lable[rate%4]);
                break;
            case BLUE:
                printf(PROGRESS_BLUE"[  %s] [%d%%] %c\r"PROGRESS_RESETCLOUR, 
                        progress_bar, rate, lable[rate%4]);
                break;
            default:
                break;
        }
        progress_bar[bar_count] = progress_bar_data.mark;
        fflush(stdout);
        rate++;
        bar_count = ((float)progress_bar_data.length / 100) * rate;
        usleep(progress_bar_data.speed*progress_bar_data.speed*1000+500);
    }
    printf("\n");

    return SUCCESS;
}

/*
 * @brief	reset the progress bar
 * @param	void
 * @return	void
 * */
void progress_bar_reset(void)
{
    printf(PROGRESS_GREEN"progress BAR DONE!\n"PROGRESS_RESETCLOUR);

    entrance_param_data.mark = DEFAULT_MARK;
    entrance_param_data.length = DEFALUT_LENGTH;
    entrance_param_data.speed = DEFALUT_SPEED;
    entrance_param_data.color = DEFALUT_COLOR;

    progress_bar_data.mark = DEFAULT_MARK;;
    progress_bar_data.length = DEFALUT_LENGTH;
    progress_bar_data.speed = DEFALUT_SPEED;
    progress_bar_data.color = DEFALUT_COLOR;

}

/*
 * @brief	Measuring terminal width
 * @param	void
 * @return	Terminal width
 * */
static int gain_terminal_width(void)
{
    struct winsize size;

	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) < 0) {
		perror("IOCTL TIOCGWINSZ ERROR!");
		return FAIL;
	}

	return size.ws_col;
}